问题场景
在使用 java 序列化进行深拷贝时,出现了类异常,现在将代码简化后进行复现:
1 | import org.springframework.boot.SpringApplication; |
如果将以上代码中 SpringApplication.run(HackthonApplication.class, args); 注释掉,运行结果是:
1 | out from sun.misc.Launcher$AppClassLoader@349885916 |
但如果加入 SpringApplication.run(HackthonApplication.class, args); ,运行时则会抛出异常:
1 | Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException |
进行 debug 调试,发现其中原对象 impl 的 classloader 的类是 org.springframework.boot.devtools.restart.classloader.RestartClassLoader
,而深拷贝出的 copy 的类的 classloader 的类是 sun.misc.Launcher$AppClassLoader
。原因是 gradle 依赖里添加了 `spring-boot-devtools :
1 | developmentOnly 'org.springframework.boot:spring-boot-devtools'` |
取消该依赖后问题消失。
追溯问题
查看 SerializationUtils.deserialize 源码:
1 |
|
发现其中使用的原生 ObjectInputStream 。查看 readObject 方法,可以看到输出 object 的 class 在 resolveClass 方法中获取(代理类通过 resolveProxyClass 方法获取):
1 | protected Class<?> resolveClass(ObjectStreamClass desc) |
其中 latestUserDefinedLoader() 方法的返回值即反序列化的对象的类的类加载器:
1 | private static ClassLoader latestUserDefinedLoader() { |
该 native 方法的注释如下:
1 | * Returns first non-privileged class loader on the stack (excluding |
解决
使用指定 ClassLoader 的 OnjectIutputStream 即可,推荐使用 org.springframework.core.ConfigurableObjectInputStream
:
1 | import org.springframework.core.ConfigurableObjectInputStream; |